home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / cvs-1_3.lha / cvs-1.3 / src / modules.c < prev    next >
C/C++ Source or Header  |  1992-04-09  |  19KB  |  811 lines

  1. /*
  2.  *    Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  *    Copyright (c) 1989-1992, Brian Berliner
  4.  *
  5.  *    You may distribute under the terms of the GNU General Public License
  6.  *    as specified in the README file that comes with the CVS 1.3 kit.
  7.  *
  8.  * Modules
  9.  *
  10.  *    Functions for accessing the modules file.
  11.  *
  12.  *    The modules file supports basically three formats of lines:
  13.  *        key [options] directory files... [ -x directory [files] ] ...
  14.  *        key [options] directory [ -x directory [files] ] ...
  15.  *        key -a aliases...
  16.  *
  17.  *    The -a option allows an aliasing step in the parsing of the modules
  18.  *    file.  The "aliases" listed on a line following the -a are
  19.  *    processed one-by-one, as if they were specified as arguments on the
  20.  *    command line.
  21.  */
  22.  
  23. #include "cvs.h"
  24.  
  25. #ifndef lint
  26. static char rcsid[] = "@(#)modules.c 1.57 92/04/10";
  27. #endif
  28.  
  29. struct sortrec
  30. {
  31.     char *modname;
  32.     char *status;
  33.     char *rest;
  34.     char *comment;
  35. };
  36.  
  37. #if __STDC__
  38. static int sort_order (CONST PTR l, CONST PTR r);
  39. static void save_d (char *k, int ks, char *d, int ds);
  40. #else
  41. static int sort_order ();
  42. static void save_d ();
  43. #endif                /* __STDC__ */
  44.  
  45.  
  46. /*
  47.  * Open the modules file, and die if the CVSROOT environment variable
  48.  * was not set.  If the modules file does not exist, that's fine, and
  49.  * a warning message is displayed and a NULL is returned.
  50.  */
  51. DBM *
  52. open_module ()
  53. {
  54.     char mfile[PATH_MAX];
  55.  
  56.     if (CVSroot == NULL)
  57.     {
  58.     (void) fprintf (stderr, 
  59.             "%s: must set the CVSROOT environment variable\n",
  60.             program_name);
  61.     error (1, 0, "or specify the '-d' option to %s", program_name);
  62.     }
  63.     (void) sprintf (mfile, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_MODULES);
  64.     return (dbm_open (mfile, O_RDONLY, 0666));
  65. }
  66.  
  67. /*
  68.  * Close the modules file, if the open succeeded, that is
  69.  */
  70. void
  71. close_module (db)
  72.     DBM *db;
  73. {
  74.     if (db != NULL)
  75.     dbm_close (db);
  76. }
  77.  
  78. /*
  79.  * This is the recursive function that processes a module name.
  80.  * It calls back the passed routine for each directory of a module
  81.  * It runs the post checkout or post tag proc from the modules file
  82.  */
  83. int
  84. do_module (db, mname, m_type, msg, callback_proc, where,
  85.        shorten, local_specified, run_module_prog, extra_arg)
  86.     DBM *db;
  87.     char *mname;
  88.     enum mtype m_type;
  89.     char *msg;
  90.     int (*callback_proc) ();
  91.     char *where;
  92.     int shorten;
  93.     int local_specified;
  94.     int run_module_prog;
  95.     char *extra_arg;
  96. {
  97.     char *checkin_prog = NULL;
  98.     char *checkout_prog = NULL;
  99.     char *tag_prog = NULL;
  100.     char *update_prog = NULL;
  101.     char cwd[PATH_MAX];
  102.     char line[MAXLINELEN];
  103.     char *xmodargv[MAXFILEPERDIR];
  104.     char **modargv;
  105.     char *value;
  106.     char *zvalue;
  107.     char *mwhere = NULL;
  108.     char *mfile = NULL;
  109.     char *spec_opt = NULL;
  110.     char xvalue[PATH_MAX];
  111.     int modargc, alias = 0;
  112.     datum key, val;
  113.     char *cp;
  114.     int c, err = 0;
  115.  
  116.     /* remember where we start */
  117.     if (getwd (cwd) == NULL)
  118.     error (1, 0, "cannot get current working directory: %s", cwd);
  119.  
  120.     /* strip extra stuff from the module name */
  121.     strip_path (mname);
  122.  
  123.     /*
  124.      * Look up the module using the following scheme:
  125.      *    1) look for mname as a module name
  126.      *    2) look for mname as a directory
  127.      *    3) look for mname as a file
  128.      *  4) take mname up to the first slash and look it up as a module name
  129.      *       (this is for checking out only part of a module)
  130.      */
  131.  
  132.     /* look it up as a module name */
  133.     key.dptr = mname;
  134.     key.dsize = strlen (key.dptr);
  135.     if (db != NULL)
  136.     val = dbm_fetch (db, key);
  137.     else
  138.     val.dptr = NULL;
  139.     if (val.dptr != NULL)
  140.     {
  141.     /* null terminate the value  XXX - is this space ours? */
  142.     val.dptr[val.dsize] = '\0';
  143.  
  144.     /* If the line ends in a comment, strip it off */
  145.     if ((cp = index (val.dptr, '#')) != NULL)
  146.     {
  147.         do
  148.         *cp-- = '\0';
  149.         while (isspace (*cp));
  150.     }
  151.     value = val.dptr;
  152.     mwhere = xstrdup (mname);
  153.     goto found;
  154.     }
  155.     else
  156.     {
  157.     char file[PATH_MAX];
  158.     char attic_file[PATH_MAX];
  159.     char *acp;
  160.  
  161.     /* check to see if mname is a directory or file */
  162.  
  163.     (void) sprintf (file, "%s/%s", CVSroot, mname);
  164.     if ((acp = rindex (mname, '/')) != NULL)
  165.     {
  166.         *acp = '\0';
  167.         (void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot, mname,
  168.                 CVSATTIC, acp + 1, RCSEXT);
  169.         *acp = '/';
  170.     }
  171.     else
  172.         (void) sprintf (attic_file, "%s/%s/%s%s", CVSroot, CVSATTIC,
  173.                 mname, RCSEXT);
  174.  
  175.     if (isdir (file))
  176.     {
  177.         value = mname;
  178.         goto found;
  179.     }
  180.     else
  181.     {
  182.         (void) strcat (file, RCSEXT);
  183.         if (isfile (file) || isfile (attic_file))
  184.         {
  185.         /* if mname was a file, we have to split it into "dir file" */
  186.         if ((cp = rindex (mname, '/')) != NULL && cp != mname)
  187.         {
  188.             char *slashp;
  189.  
  190.             /* put the ' ' in a copy so we don't mess up the original */
  191.             value = strcpy (xvalue, mname);
  192.             slashp = rindex (value, '/');
  193.             *slashp = ' ';
  194.         }
  195.         else
  196.         {
  197.             /*
  198.              * the only '/' at the beginning or no '/' at all
  199.              * means the file we are interested in is in CVSROOT
  200.              * itself so the directory should be '.'
  201.              */
  202.             if (cp == mname)
  203.             {
  204.             /* drop the leading / if specified */
  205.             value = strcpy (xvalue, ". ");
  206.             (void) strcat (xvalue, mname + 1);
  207.             }
  208.             else
  209.             {
  210.             /* otherwise just copy it */
  211.             value = strcpy (xvalue, ". ");
  212.             (void) strcat (xvalue, mname);
  213.             }
  214.         }
  215.         goto found;
  216.         }
  217.     }
  218.     }
  219.  
  220.     /* look up everything to the first / as a module */
  221.     if (mname[0] != '/' && (cp = index (mname, '/')) != NULL)
  222.     {
  223.     /* Make the slash the new end of the string temporarily */
  224.     *cp = '\0';
  225.     key.dptr = mname;
  226.     key.dsize = strlen (key.dptr);
  227.  
  228.     /* do the lookup */
  229.     if (db != NULL)
  230.         val = dbm_fetch (db, key);
  231.     else
  232.         val.dptr = NULL;
  233.  
  234.     /* if we found it, clean up the value and life is good */
  235.     if (val.dptr != NULL)
  236.     {
  237.         char *cp2;
  238.  
  239.         /* null terminate the value XXX - is this space ours? */
  240.         val.dptr[val.dsize] = '\0';
  241.  
  242.         /* If the line ends in a comment, strip it off */
  243.         if ((cp2 = index (val.dptr, '#')) != NULL)
  244.         {
  245.         do
  246.             *cp2-- = '\0';
  247.         while (isspace (*cp2));
  248.         }
  249.         value = val.dptr;
  250.  
  251.         /* mwhere gets just the module name */
  252.         mwhere = xstrdup (mname);
  253.         mfile = cp + 1;
  254.  
  255.         /* put the / back in mname */
  256.         *cp = '/';
  257.  
  258.         goto found;
  259.     }
  260.  
  261.     /* put the / back in mname */
  262.     *cp = '/';
  263.     }
  264.  
  265.     /* if we got here, we couldn't find it using our search, so give up */
  266.     error (0, 0, "cannot find module `%s' - ignored", mname);
  267.     err++;
  268.     if (mwhere)
  269.     free (mwhere);
  270.     return (err);
  271.  
  272.  
  273.     /*
  274.      * At this point, we found what we were looking for in one
  275.      * of the many different forms.
  276.      */
  277.   found:
  278.  
  279.     /* copy value to our own string since if we go recursive we'll be
  280.        really screwed if we do another dbm lookup */
  281.     zvalue = xstrdup (value);
  282.     value = zvalue;
  283.  
  284.     /* search the value for the special delimiter and save for later */
  285.     if ((cp = index (value, CVSMODULE_SPEC)) != NULL)
  286.     {
  287.     *cp = '\0';            /* null out the special char */
  288.     spec_opt = cp + 1;        /* save the options for later */
  289.  
  290.     if (cp != value)        /* strip whitespace if necessary */
  291.         while (isspace (*--cp))
  292.         *cp = '\0';
  293.  
  294.     if (cp == value)
  295.     {
  296.         /*
  297.          * we had nothing but special options, so skip arg
  298.          * parsing and regular stuff entirely
  299.          *
  300.          * If there were only special ones though, we must
  301.          * make the appropriate directory and cd to it
  302.          */
  303.         char *dir;
  304.  
  305.         /* XXX - XXX - MAJOR HACK - DO NOT SHIP - this needs to
  306.            be !pipeout, but we don't know that here yet */
  307.         if (!run_module_prog)
  308.         goto out;
  309.  
  310.         dir = where ? where : mname;
  311.         /* XXX - think about making null repositories at each dir here
  312.              instead of just at the bottom */
  313.         make_directories (dir);
  314.         if (chdir (dir) < 0)
  315.         {
  316.         error (0, errno, "cannot chdir to %s", dir);
  317.         spec_opt = NULL;
  318.         err++;
  319.         goto out;
  320.         }
  321.         if (!isfile (CVSADM) && !isfile (OCVSADM))
  322.         {
  323.         char nullrepos[PATH_MAX];
  324.  
  325.         (void) sprintf (nullrepos, "%s/%s/%s", CVSroot,
  326.                 CVSROOTADM, CVSNULLREPOS);
  327.         if (!isfile (nullrepos))
  328.             (void) mkdir (nullrepos, 0777);
  329.         Create_Admin (".", nullrepos, (char *) NULL, (char *) NULL);
  330.         if (!noexec)
  331.         {
  332.             FILE *fp;
  333.  
  334.             fp = open_file (CVSADM_ENTSTAT, "w+");
  335.             if (fclose (fp) == EOF)
  336.             error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
  337.         }
  338.         }
  339.       out:
  340.         goto do_special;
  341.     }
  342.     }
  343.  
  344.     /* don't do special options only part of a module was specified */
  345.     if (mfile != NULL)
  346.     spec_opt = NULL;
  347.  
  348.     /*
  349.      * value now contains one of the following:
  350.      *    1) dir
  351.      *      2) dir file
  352.      *    3) the value from modules without any special args
  353.      *            [ args ] dir [file] [file] ...
  354.      *         or     -a module [ module ] ...
  355.      */
  356.  
  357.     /* Put the value on a line with XXX prepended for getopt to eat */
  358.     (void) sprintf (line, "%s %s", "XXX", value);
  359.  
  360.     /* turn the line into an argv[] array */
  361.     line2argv (&modargc, xmodargv, line);
  362.     modargv = xmodargv;
  363.  
  364.     /* parse the args */
  365.     optind = 1;
  366.     while ((c = gnu_getopt (modargc, modargv, CVSMODULE_OPTS)) != -1)
  367.     {
  368.     switch (c)
  369.     {
  370.         case 'a':
  371.         alias = 1;
  372.         break;
  373.         case 'd':
  374.         if (mwhere)
  375.             free (mwhere);
  376.         mwhere = xstrdup (optarg);
  377.         break;
  378.         case 'i':
  379.         checkin_prog = optarg;
  380.         break;
  381.         case 'l':
  382.         local_specified = 1;
  383.         case 'o':
  384.         checkout_prog = optarg;
  385.         break;
  386.         case 't':
  387.         tag_prog = optarg;
  388.         break;
  389.         case 'u':
  390.         update_prog = optarg;
  391.         break;
  392.         case '?':
  393.         error (0, 0,
  394.                "modules file has invalid option for key %s value %s",
  395.                key.dptr, val.dptr);
  396.         err++;
  397.         if (mwhere)
  398.             free (mwhere);
  399.         free (zvalue);
  400.         return (err);
  401.     }
  402.     }
  403.     modargc -= optind;
  404.     modargv += optind;
  405.     if (modargc == 0)
  406.     {
  407.     error (0, 0, "modules file missing directory for module %s", mname);
  408.     if (mwhere)
  409.         free (mwhere);
  410.     free (zvalue);
  411.     return (++err);
  412.     }
  413.  
  414.     /* if this was an alias, call ourselves recursively for each module */
  415.     if (alias)
  416.     {
  417.     int i;
  418.  
  419.     for (i = 0; i < modargc; i++)
  420.         err += do_module (db, modargv[i], m_type, msg, callback_proc,
  421.                   where, shorten, local_specified,
  422.                   run_module_prog, extra_arg);
  423.     if (mwhere)
  424.         free (mwhere);
  425.     free (zvalue);
  426.     return (err);
  427.     }
  428.  
  429.     /* otherwise, process this module */
  430.     err += callback_proc (&modargc, modargv, where, mwhere, mfile, shorten,
  431.               local_specified, mname, msg);
  432.  
  433.     /* clean up */
  434.     free_names (&modargc, modargv);
  435.  
  436.     /* if there were special include args, process them now */
  437.  
  438.   do_special:
  439.  
  440.     /* blow off special options if -l was specified */
  441.     if (local_specified)
  442.     spec_opt = NULL;
  443.  
  444.     while (spec_opt != NULL)
  445.     {
  446.     char *next_opt;
  447.  
  448.     cp = index (spec_opt, CVSMODULE_SPEC);
  449.     if (cp != NULL)
  450.     {
  451.         /* save the beginning of the next arg */
  452.         next_opt = cp + 1;
  453.  
  454.         /* strip whitespace off the end */
  455.         do
  456.         *cp = '\0';
  457.         while (isspace (*--cp));
  458.     }
  459.     else
  460.         next_opt = NULL;
  461.  
  462.     /* strip whitespace from front */
  463.     while (isspace (*spec_opt))
  464.         spec_opt++;
  465.  
  466.     if (*spec_opt == '\0')
  467.         error (0, 0, "Mal-formed %c option for module %s - ignored",
  468.            CVSMODULE_SPEC, mname);
  469.     else
  470.         err += do_module (db, spec_opt, m_type, msg, callback_proc,
  471.                   (char *) NULL, 0, local_specified,
  472.                   run_module_prog, extra_arg);
  473.     spec_opt = next_opt;
  474.     }
  475.  
  476.     /* write out the checkin/update prog files if necessary */
  477.     if (err == 0 && !noexec && m_type == CHECKOUT && run_module_prog)
  478.     {
  479.     FILE *fp;
  480.  
  481.     if (checkin_prog != NULL)
  482.     {
  483.         fp = open_file (CVSADM_CIPROG, "w+");
  484.         (void) fprintf (fp, "%s\n", checkin_prog);
  485.         if (fclose (fp) == EOF)
  486.         error (1, errno, "cannot close %s", CVSADM_CIPROG);
  487.     }
  488.     if (update_prog != NULL)
  489.     {
  490.         fp = open_file (CVSADM_UPROG, "w+");
  491.         (void) fprintf (fp, "%s\n", update_prog);
  492.         if (fclose (fp) == EOF)
  493.         error (1, errno, "cannot close %s", CVSADM_UPROG);
  494.     }
  495.     }
  496.  
  497.     /* cd back to where we started */
  498.     if (chdir (cwd) < 0)
  499.     error (1, errno, "failed chdir to %s!", cwd);
  500.  
  501.     /* run checkout or tag prog if appropriate */
  502.     if (err == 0 && run_module_prog)
  503.     {
  504.     if ((m_type == TAG && tag_prog != NULL) ||
  505.         (m_type == CHECKOUT && checkout_prog != NULL))
  506.     {
  507.         /*
  508.          * If a relative pathname is specified as the checkout or
  509.          * tag proc, try to tack on the current "where" value.
  510.          * if we can't find a matching program, just punt and use
  511.          * whatever is specified in the modules file.
  512.          */
  513.         char real_prog[PATH_MAX];
  514.         char *prog = (m_type == TAG ? tag_prog : checkout_prog);
  515.         char *real_where = (where != NULL ? where : mwhere);
  516.  
  517.         if ((*prog != '/') && (*prog != '.'))
  518.         {
  519.         (void) sprintf (real_prog, "%s/%s", real_where, prog);
  520.         if (isfile (real_prog))
  521.             prog = real_prog;
  522.         }
  523.  
  524.         run_setup ("%s %s", prog, real_where);
  525.         if (extra_arg)
  526.         run_arg (extra_arg);
  527.  
  528.         if (!quiet)
  529.         {
  530.         (void) printf ("%s %s: Executing '", program_name,
  531.                    command_name);
  532.         run_print (stdout);
  533.         (void) printf ("'\n");
  534.         }
  535.         err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  536.     }
  537.     }
  538.  
  539.     /* clean up */
  540.     if (mwhere)
  541.     free (mwhere);
  542.     free (zvalue);
  543.  
  544.     return (err);
  545. }
  546.  
  547. /* - Read all the records from the modules database into an array.
  548.    - Sort the array depending on what format is desired.
  549.    - Print the array in the format desired.
  550.  
  551.    Currently, there are only two "desires":
  552.  
  553.    1. Sort by module name and format the whole entry including switches,
  554.       files and the comment field: (Including aliases)
  555.  
  556.       modulename    -s switches, one per line, even if
  557.             -i it has many switches.
  558.             Directories and files involved, formatted
  559.             to cover multiple lines if necessary.
  560.             # Comment, also formatted to cover multiple
  561.             # lines if necessary.
  562.  
  563.    2. Sort by status field string and print:  (*not* including aliases)
  564.  
  565.       modulename    STATUS    Directories and files involved, formatted
  566.                 to cover multiple lines if necessary.
  567.                 # Comment, also formatted to cover multiple
  568.                 # lines if necessary.
  569. */
  570.  
  571. static struct sortrec *s_head;
  572.  
  573. static int s_max = 0;            /* Number of elements allocated */
  574. static int s_count = 0;            /* Number of elements used */
  575.  
  576. static int Status;
  577. static char def_status[] = "NONE";
  578.  
  579. /* Sort routine for qsort:
  580.    - If we want the "Status" field to be sorted, check it first.
  581.    - Then compare the "module name" fields.  Since they are unique, we don't
  582.      have to look further.
  583. */
  584. static int
  585. sort_order (l, r)
  586.     CONST PTR l;
  587.     CONST PTR r;
  588. {
  589.     int i;
  590.     CONST struct sortrec *left = (CONST struct sortrec *) l;
  591.     CONST struct sortrec *right = (CONST struct sortrec *) r;
  592.  
  593.     if (Status)
  594.     {
  595.     /* If Sort by status field, compare them. */
  596.     if ((i = strcmp (left->status, right->status)) != 0)
  597.         return (i);
  598.     }
  599.     return (strcmp (left->modname, right->modname));
  600. }
  601.  
  602. static void
  603. save_d (k, ks, d, ds)
  604.     char *k;
  605.     int ks;
  606.     char *d;
  607.     int ds;
  608. {
  609.     char *cp, *cp2;
  610.     struct sortrec *s_rec;
  611.  
  612.     if (Status && *d == '-' && *(d + 1) == 'a')
  613.     return;                /* We want "cvs co -s" and it is an alias! */
  614.  
  615.     if (s_count == s_max)
  616.     {
  617.     s_max += 64;
  618.     s_head = (struct sortrec *) xrealloc ((char *) s_head, s_max * sizeof (*s_head));
  619.     }
  620.     s_rec = &s_head[s_count];
  621.     s_rec->modname = cp = xmalloc (ks + 1);
  622.     (void) strncpy (cp, k, ks);
  623.     *(cp + ks) = '\0';
  624.  
  625.     s_rec->rest = cp2 = xmalloc (ds + 1);
  626.     cp = d;
  627.     *(cp + ds) = '\0';    /* Assumes an extra byte at end of static dbm buffer */
  628.  
  629.     while (isspace (*cp))
  630.     cp++;
  631.     /* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */
  632.     while (*cp)
  633.     {
  634.     if (isspace (*cp))
  635.     {
  636.         *cp2++ = ' ';
  637.         while (isspace (*cp))
  638.         cp++;
  639.     }
  640.     else
  641.         *cp2++ = *cp++;
  642.     }
  643.     *cp2 = '\0';
  644.  
  645.     /* Look for the "-s statusvalue" text */
  646.     if (Status)
  647.     {
  648.     s_rec->status = def_status;
  649.  
  650.     /* Minor kluge, but general enough to maintain */
  651.     for (cp = s_rec->rest; (cp2 = index (cp, '-')) != NULL; cp = ++cp2)
  652.     {
  653.         if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ')
  654.         {
  655.         s_rec->status = (cp2 += 3);
  656.         while (*cp2 != ' ')
  657.             cp2++;
  658.         *cp2++ = '\0';
  659.         cp = cp2;
  660.         break;
  661.         }
  662.     }
  663.     }
  664.     else
  665.     cp = s_rec->rest;
  666.  
  667.     /* Find comment field, clean up on all three sides & compress blanks */
  668.     if ((cp2 = cp = index (cp, '#')) != NULL)
  669.     {
  670.     if (*--cp2 == ' ')
  671.         *cp2 = '\0';
  672.     if (*++cp == ' ')
  673.         cp++;
  674.     s_rec->comment = cp;
  675.     }
  676.     else
  677.     s_rec->comment = "";
  678.  
  679.     s_count++;
  680. }
  681.  
  682. void
  683. cat_module (status)
  684.     int status;
  685. {
  686.     DBM *db;
  687.     datum key, val;
  688.     int i, c, wid, argc, cols = 80, indent, fill;
  689.     int moduleargc;
  690.     struct sortrec *s_h;
  691.     char *cp, *cp2, **argv;
  692.     char line[MAXLINELEN], *moduleargv[MAXFILEPERDIR];
  693.  
  694. #ifdef sun
  695. #ifdef TIOCGSIZE
  696.     struct ttysize ts;
  697.  
  698.     (void) ioctl (0, TIOCGSIZE, &ts);
  699.     cols = ts.ts_cols;
  700. #endif
  701. #else
  702. #ifdef TIOCGWINSZ
  703.     struct winsize ws;
  704.  
  705.     (void) ioctl (0, TIOCGWINSZ, &ws);
  706.     cols = ws.ws_col;
  707. #endif
  708. #endif
  709.  
  710.     Status = status;
  711.  
  712.     /* Read the whole modules file into allocated records */
  713.     if (!(db = open_module ()))
  714.     error (1, 0, "failed to open the modules file");
  715.  
  716.     for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_nextkey (db))
  717.     {
  718.     val = dbm_fetch (db, key);
  719.     if (val.dptr != NULL)
  720.         save_d (key.dptr, key.dsize, val.dptr, val.dsize);
  721.     }
  722.  
  723.     /* Sort the list as requested */
  724.     qsort ((PTR) s_head, s_count, sizeof (struct sortrec), sort_order);
  725.  
  726.     /*
  727.      * Run through the sorted array and format the entries
  728.      * indent = space for modulename + space for status field
  729.      */
  730.     indent = 12 + (status * 12);
  731.     fill = cols - (indent + 2);
  732.     for (s_h = s_head, i = 0; i < s_count; i++, s_h++)
  733.     {
  734.     /* Print module name (and status, if wanted) */
  735.     (void) printf ("%-12s", s_h->modname);
  736.     if (status)
  737.     {
  738.         (void) printf (" %-11s", s_h->status);
  739.         if (s_h->status != def_status)
  740.         *(s_h->status + strlen (s_h->status)) = ' ';
  741.     }
  742.  
  743.     /* Parse module file entry as command line and print options */
  744.     (void) sprintf (line, "%s %s", s_h->modname, s_h->rest);
  745.     line2argv (&moduleargc, moduleargv, line);
  746.     argc = moduleargc;
  747.     argv = moduleargv;
  748.  
  749.     optind = 1;
  750.     wid = 0;
  751.     while ((c = gnu_getopt (argc, argv, CVSMODULE_OPTS)) != -1)
  752.     {
  753.         if (!status)
  754.         {
  755.         if (c == 'a')
  756.         {
  757.             (void) printf (" -a");
  758.             wid += 3;        /* Could just set it to 3 */
  759.         }
  760.         else
  761.         {
  762.             if (strlen (optarg) + 4 + wid > (unsigned) fill)
  763.             {
  764.             (void) printf ("\n%*s", indent, "");
  765.             wid = 0;
  766.             }
  767.             (void) printf (" -%c %s", c, optarg);
  768.             wid += strlen (optarg) + 4;
  769.         }
  770.         }
  771.     }
  772.     argc -= optind;
  773.     argv += optind;
  774.  
  775.     /* Format and Print all the files and directories */
  776.     for (; argc--; argv++)
  777.     {
  778.         if (strlen (*argv) + wid > (unsigned) fill)
  779.         {
  780.         (void) printf ("\n%*s", indent, "");
  781.         wid = 0;
  782.         }
  783.         (void) printf (" %s", *argv);
  784.         wid += strlen (*argv) + 1;
  785.     }
  786.     (void) printf ("\n");
  787.  
  788.     /* Format the comment field -- save_d (), compressed spaces */
  789.     for (cp2 = cp = s_h->comment; *cp; cp2 = cp)
  790.     {
  791.         (void) printf ("%*s # ", indent, "");
  792.         if (strlen (cp2) < (unsigned) (fill - 2))
  793.         {
  794.         (void) printf ("%s\n", cp2);
  795.         break;
  796.         }
  797.         cp += fill - 2;
  798.         while (*cp != ' ' && cp > cp2)
  799.         cp--;
  800.         if (cp == cp2)
  801.         {
  802.         (void) printf ("%s\n", cp2);
  803.         break;
  804.         }
  805.  
  806.         *cp++ = '\0';
  807.         (void) printf ("%s\n", cp2);
  808.     }
  809.     }
  810. }
  811.